package dao

import (
	"common/biz"
	"common/logs"
	"common/utils"
	"context"
	"core/models/db"
	"core/models/entity"
	"core/repo"
	"fmt"
	"framework/msError"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"gorm.io/gorm"
	"hall/models/request"
	"time"
)

type NewsDao struct {
	repo *repo.Manager
	base *BaseDao
}

func (d *NewsDao) NoticeAll(ctx context.Context) *[]db.Notice {
	data := new([]db.Notice)
	d.repo.DB.Client.WithContext(ctx).Where("status=? AND type=1", true).Find(data)
	return data
}

func (d *NewsDao) GetNotice(ctx context.Context, id uint) *db.Notice {
	data := new(db.Notice)
	d.repo.DB.Client.WithContext(ctx).Where("id=?", id).First(data)
	return data
}

func (d *NewsDao) EmailList(ctx context.Context, uid uint, req *request.PageMsg) (int64, *[]db.UserNotice) {
	data := new([]db.UserNotice)
	var count int64
	_db := d.repo.DB.Client.WithContext(ctx).Model(db.UserNotice{}).Where("uid=?", uid)
	_db.Where("deleted_at IS NULL").Count(&count)
	_db.Scopes(db.Paginate(req.Page, req.PageSize)).
		Order("status ASC").
		Find(data)
	return count, data
}

func (d *NewsDao) GetEmail(ctx context.Context, id uint, uid uint) *db.UserNotice {
	data := new(db.UserNotice)
	_db := d.repo.DB.Client.WithContext(ctx).Model(data).Where("id=? AND uid=?", id, uid)
	_db.Updates(map[string]any{
		"read_time": time.Now(),
		"status":    true,
	})
	_db.First(data)
	return data
}

func (d *NewsDao) EmailReceive(ctx context.Context, uid uint, receiveID, id uint) *msError.Error {
	_db := d.repo.DB.Client.WithContext(ctx)
	tx := _db.Begin()
	_mdb := d.repo.Mongo
	data := d.GetNoticeTemplate(ctx, receiveID)
	var err error
	if data == nil || data.ID == 0 {
		logs.Error("[newsHandler]用户%d EmailReceive err:获取数据失败 req=%v", uid, data)
		return biz.RequestDataError
	}
	//修改状态为已领取
	err = tx.Model(db.UserNotice{}).Where("id=?", id).Updates(map[string]any{
		"is_receive":   2,
		"receive_time": time.Now().Format(time.DateTime),
	}).Error
	if err != nil {
		logs.Error("[newsHandler]用户%d EmailReceive err:修改状态失败 dbErr=%v", uid, err)
		tx.Rollback()
		return biz.SqlError
	}

	var haxCards []uint
	for _, v := range data.Reward {
		switch v.Types.Type {
		case 1: //卡牌
			typeCard := new(db.TypeCard)
			_db.Model(typeCard).Where("id=? AND type_id=?", v.TypeCardID, v.TypeID).First(typeCard)
			if typeCard == nil || typeCard.ID == 0 {
				logs.Error("[newsHandler]用户%d EmailReceive err:获取数据失败 req=%v", uid, typeCard)
				return biz.RequestDataError
			}
			rate := typeCard.Output
			if typeCard.Output != typeCard.OutputMax {
				rate = utils.Random(int(typeCard.Output), int(typeCard.OutputMax))
			}
			for i := 0; i < v.Num; i++ {
				card := &db.WarehouseCard{
					UID:        uid,
					TypeID:     v.TypeID,
					TypeCardID: v.TypeCardID,
					Num:        typeCard.TotalOutput,
					Rate:       rate,
					IsNew:      true,
				}
				err = tx.Model(card).Create(card).Error
				if err != nil {
					logs.Error("[newsHandler]用户%d EmailReceive err:添加卡牌失败 req=%v,dbErr=%v", uid, v, err)
					tx.Rollback()
					return biz.SqlError
				}
				//NFT添加一个tokenID
				widLen := len(fmt.Sprintf("%d", card.ID))
				var widStr string
				if widLen < 8 {
					for i := 0; i < 8-widLen; i++ {
						widStr += "0"
					}
				}
				widStr += fmt.Sprintf("%d", card.ID)
				tokenID := fmt.Sprintf("S%v", widStr)
				card.TokenID = &tokenID
				err = tx.Save(card).Error
				if err != nil {
					err = fmt.Errorf("[newsHandler]用户%d EmailReceive err:上链失败,res=%v,dbErr:%v", uid, typeCard, err)
					break
				}
				haxCards = append(haxCards, card.ID)
			}
			break
		case 2: //材料
			material := new(db.WarehouseMaterial)
			materialType := new(db.WarehouseMaterialHasOneType)
			_db.Model(material).Preload("Types").Where("uid=? AND type_id=?", uid, v.TypeID).First(materialType)
			if materialType == nil || materialType.ID == 0 { //存在修改
				err = tx.Model(material).Create(&db.WarehouseMaterial{
					UID:    uid,
					TypeID: v.TypeID,
					Num:    int64(v.Num),
				}).Error
				_db.Model(material).Preload("Types").Where("uid=? AND type_id=?", uid, v.TypeID).First(materialType)
			} else { //不存在 添加
				tx.Model(material).Where("uid=? AND type_id=?", uid, v.TypeID).Update("num", gorm.Expr("num+?", int64(v.Num)))
			}
			if err != nil {
				logs.Error("[newsHandler]用户%d EmailReceive err:修改材料数量错误 req=%v,dbErr=%v", uid, v, err)
				tx.Rollback()
				return biz.SqlError
			}
			//添加仓库材料记录
			collection := _mdb.Db.Collection("material_log")
			materialLog := &entity.MaterialLog{
				UID:        uid,
				WID:        materialType.ID,
				TypeID:     materialType.TypeID,
				TypeName:   materialType.Types.Name,
				TypeImg:    materialType.Types.Img,
				TypeModule: materialType.Types.Module,
				LinkID:     fmt.Sprintf("%d", id),
				LinkTable:  db.TableNameUserNotice,
				Ago:        materialType.Num,
				Num:        int64(v.Num),
				After:      materialType.Num + int64(v.Num),
				Remarks:    "邮件-领取奖励",
				AddTime:    time.Now().Format(time.DateTime),
			}
			_, err = collection.InsertOne(ctx, materialLog)
			if err != nil {
				logs.Error("[newsHandler]用户%d EmailReceive err:添加材料记录失败 req=%v,dbErr=%v", uid, v, err)
				tx.Rollback()
				return biz.SqlError
			}
			break
		case 3: //装备
			typeEquip := new(db.TypeEquip)
			_db.Model(typeEquip).Where("type_id=?", v.TypeID).First(typeEquip)
			if typeEquip == nil || typeEquip.ID == 0 {
				logs.Error("[newsHandler]用户%d EmailReceive err:获取数据失败 req=%v", uid, typeEquip)
				return biz.RequestDataError
			}
			for i := 0; i < v.Num; i++ {
				equip := &db.WarehouseEquip{
					UID:    uid,
					TypeID: v.TypeID,
					IsNew:  true,
					SyNum:  typeEquip.Term,
				}
				err = tx.Model(equip).Create(equip).Error
				if err != nil {
					logs.Error("[newsHandler]用户%d EmailReceive err:添加装备失败 req=%v,dbErr=%v", uid, v, err)
					tx.Rollback()
					return biz.SqlError
				}
			}
			break
		}
	}
	//// TODO 4.上链
	//for _, v := range haxCards {
	//	go d.base.TimingObtainHax(uid, v)
	//}
	tx.Commit()
	return nil
}

func (d *NewsDao) GetNoticeTemplate(ctx context.Context, id uint) *db.NoticeTemplateHasManReward {
	data := new(db.NoticeTemplateHasManReward)
	d.repo.DB.Client.WithContext(ctx).Model(db.NoticeTemplate{}).Preload("Reward").Preload("Reward.Types").Where("id=?", id).First(&data)
	return data
}

func (d *NewsDao) EmailRemoveAll(ctx context.Context, uid uint) error {
	data := new(db.UserNotice)
	return d.repo.DB.Client.WithContext(ctx).Model(data).Where("uid=? AND status=? AND is_receive<>1", uid, true).Delete(data).Error
}

func (d *NewsDao) EmailRemove(ctx context.Context, uid uint, id uint) error {
	data := new(db.UserNotice)
	return d.repo.DB.Client.WithContext(ctx).Model(data).Where("id=? AND uid=? AND status=? AND is_receive<>1", id, uid, true).Delete(data).Error
}

func (d *NewsDao) MongoOutputList(ctx context.Context, uid uint, req request.PageMsg) (int64, *[]entity.ProductionOutput) {
	collection := d.repo.Mongo.Db.Collection("production_output_log")
	whe := bson.D{
		{"uid", uid},
		{"status", true},
	}
	count, err := collection.CountDocuments(ctx, whe)
	if err != nil {
		return 0, nil
	}
	var page, pageSize = int64(req.Page), int64(req.PageSize)
	//if count >= pageSize {
	//	page = count / pageSize * pageSize
	//}
	option := options.Find()
	option.SetSort(bson.D{{"endTime", -1}}) //降序
	option.SetSkip((page - 1) * pageSize)
	option.SetLimit(pageSize)
	cur, err := collection.Find(ctx, whe, option)
	if err != nil {
		return 0, nil
	}
	var results []entity.ProductionOutput
	for cur.Next(ctx) {
		var result entity.ProductionOutput
		err = cur.Decode(&result)
		if err == mongo.ErrNoDocuments {
			return 0, nil
		}
		results = append(results, result)
	}
	return count, &results
}

func (d *NewsDao) MongoTransferLogList(ctx context.Context, uid uint, req request.PageMsg) (int64, *[]entity.TransferLog) {
	collection := d.repo.Mongo.Db.Collection("transfer_log")
	whe := bson.D{
		{"$or", bson.A{
			bson.D{
				{"uid", uid},
				{"type", 1},
			},
			bson.D{
				{"obtain", uid},
			},
		}},
	}
	count, err := collection.CountDocuments(ctx, whe)
	if err != nil {
		return 0, nil
	}
	var page, pageSize = int64(req.Page), int64(req.PageSize)
	//if count >= pageSize {
	//	page = count / pageSize * pageSize
	//}
	option := options.Find()
	option.SetSort(bson.D{{"addTime", -1}}) //降序
	option.SetSkip((page - 1) * pageSize)
	option.SetLimit(pageSize)
	cur, err := collection.Find(ctx, whe, option)
	if err != nil {
		return 0, nil
	}
	var results []entity.TransferLog
	for cur.Next(ctx) {
		var result entity.TransferLog
		err = cur.Decode(&result)
		if err == mongo.ErrNoDocuments {
			return 0, nil
		}
		results = append(results, result)
	}
	return count, &results
}

func (d *NewsDao) MongoMaterialLogList(ctx context.Context, uid uint, typeID uint, req request.PageMsg) (int64, *[]entity.MaterialLog) {
	collection := d.repo.Mongo.Db.Collection("material_log")
	whe := bson.D{
		{"uid", uid},
		{"typeID", typeID},
	}
	count, err := collection.CountDocuments(ctx, whe)
	if err != nil {
		return 0, nil
	}
	var page, pageSize = int64(req.Page), int64(req.PageSize)
	//if count >= pageSize {
	//	page = count / pageSize * pageSize
	//}
	option := options.Find()
	option.SetSort(bson.D{{"addTime", -1}}) //降序
	option.SetSkip((page - 1) * pageSize)
	option.SetLimit(pageSize)
	cur, err := collection.Find(ctx, whe, option)
	if err != nil {
		return 0, nil
	}
	var results []entity.MaterialLog
	for cur.Next(ctx) {
		var result entity.MaterialLog
		err = cur.Decode(&result)
		if err == mongo.ErrNoDocuments {
			return 0, nil
		}
		results = append(results, result)
	}
	return count, &results
}

func (d *NewsDao) AddNews(ctx context.Context, news *db.UserNotice) error {
	return d.repo.DB.Client.WithContext(ctx).Model(news).Create(news).Error
}

func (d *NewsDao) MongoOpenPackageLogList(ctx context.Context, uid uint, req request.PageMsg) (int64, *[]entity.CardPackage) {
	collection := d.repo.Mongo.Db.Collection("card_package_log")
	whe := bson.D{
		{"uid", uid},
	}
	count, err := collection.CountDocuments(ctx, whe)
	if err != nil {
		return 0, nil
	}
	var page, pageSize = int64(req.Page), int64(req.PageSize)
	option := options.Find()
	option.SetSort(bson.D{{"addTime", -1}}) //降序
	option.SetSkip((page - 1) * pageSize)
	option.SetLimit(pageSize)
	cur, err := collection.Find(ctx, whe, option)
	if err != nil {
		return 0, nil
	}
	var results []entity.CardPackage
	for cur.Next(ctx) {
		var result entity.CardPackage
		err = cur.Decode(&result)
		if err == mongo.ErrNoDocuments {
			return 0, nil
		}
		results = append(results, result)
	}
	return count, &results
}

func (d *NewsDao) MongoInvestExchangeLogList(ctx context.Context, uid uint, req request.PageMsg) (int64, *[]entity.InvestmentExchangeLog) {
	collection := d.repo.Mongo.Db.Collection("investment_exchange_log")
	whe := bson.D{
		{"uid", uid},
	}
	count, err := collection.CountDocuments(ctx, whe)
	if err != nil {
		return 0, nil
	}
	var page, pageSize = int64(req.Page), int64(req.PageSize)
	option := options.Find()
	option.SetSort(bson.D{{"addTime", -1}}) //降序
	option.SetSkip((page - 1) * pageSize)
	option.SetLimit(pageSize)
	cur, err := collection.Find(ctx, whe, option)
	if err != nil {
		return 0, nil
	}
	var results []entity.InvestmentExchangeLog
	for cur.Next(ctx) {
		var result entity.InvestmentExchangeLog
		err = cur.Decode(&result)
		if err == mongo.ErrNoDocuments {
			return 0, nil
		}
		results = append(results, result)
	}
	return count, &results
}

func (d *NewsDao) MongoLotteryLogList(ctx context.Context, uid uint, req request.PageMsg) (int64, *[]entity.UserLottery) {
	collection := d.repo.Mongo.Db.Collection("user_lottery_log")
	whe := bson.D{
		{"uid", uid},
	}
	count, err := collection.CountDocuments(ctx, whe)
	if err != nil {
		return 0, nil
	}
	var page, pageSize = int64(req.Page), int64(req.PageSize)
	option := options.Find()
	option.SetSort(bson.D{{"addTime", -1}}) //降序
	option.SetSkip((page - 1) * pageSize)
	option.SetLimit(pageSize)
	cur, err := collection.Find(ctx, whe, option)
	if err != nil {
		return 0, nil
	}
	var results []entity.UserLottery
	for cur.Next(ctx) {
		var result entity.UserLottery
		err = cur.Decode(&result)
		if err == mongo.ErrNoDocuments {
			return 0, nil
		}
		results = append(results, result)
	}
	return count, &results
}

func (d *NewsDao) MongoExtractLogList(ctx context.Context, uid uint, req request.PageMsg) (int64, any) {
	collection := d.repo.Mongo.Db.Collection("warehouse_money_extract_log")
	whe := bson.D{
		{"uid", uid},
	}
	count, err := collection.CountDocuments(ctx, whe)
	if err != nil {
		return 0, nil
	}
	var page, pageSize = int64(req.Page), int64(req.PageSize)
	option := options.Find()
	option.SetSort(bson.D{{"addTime", -1}}) //降序
	option.SetSkip((page - 1) * pageSize)
	option.SetLimit(pageSize)
	cur, err := collection.Find(ctx, whe, option)
	if err != nil {
		return 0, nil
	}
	var results []entity.WarehouseMoneyExtract
	for cur.Next(ctx) {
		var result entity.WarehouseMoneyExtract
		err = cur.Decode(&result)
		if err == mongo.ErrNoDocuments {
			return 0, nil
		}
		results = append(results, result)
	}
	return count, &results
}

func NewNewsDao(m *repo.Manager) *NewsDao {
	return &NewsDao{
		repo: m,
		base: NewBaseDao(m),
	}
}
